#include<iostream>
#include<functional>
#include<errno.h>
#include<string.h>
#include"Socket.hpp"
#include"EventLoop_Timequeue.hpp"
#include"Connection.hpp"


uint64_t conn_id = 1;//连接id
std::unordered_map<uint64_t,ConnPtr> conn_map;//保存连接的映射表



//通信套接字的回调函数
void ServCloseCallback(const ConnPtr &conn)
{
    conn_map.erase(conn->ConnId());
}
void MsgCallback(const ConnPtr &conn,Buffer *buf)
{
    char buftmp[1024];
    buf->ReadAndMove(buftmp,buf->ReadableSize());
    LOG_DBG("recv msg :%s",buftmp);
    std::string str = "hello world";
    conn->Send(str.c_str(),str.size());//发送消息
}
void ConnectCallback(const ConnPtr &conn)
{
    LOG_INF("new connection %p",conn.get());
}



//服务器监听套接字的回调函数（底层就是accept系统调用）
void Acceptor(EventLoop *loop,Channel *socket_listen_channel)
{
    int fd = socket_listen_channel->Fd();
    int newfd = accept(fd,nullptr,nullptr);
    if(newfd < 0){
        LOG_ERR("accept error:%s",strerror(errno));
        return;
    }
    LOG_INF("new connection fd:%d",newfd);
    ConnPtr conn = std::make_shared<Connection>(loop,conn_id,newfd);
    //为通信套接字设置回调函数
    conn->SetConnectedCallback(std::bind(ConnectCallback,std::placeholders::_1));
    conn->SetMessageCallback(std::bind(MsgCallback,std::placeholders::_1,std::placeholders::_2));
    conn->EnableInactiveRelease(5);
    conn->SetServerClosedCallback(std::bind(ServCloseCallback,std::placeholders::_1));
    conn->Establish();//启动连接(包含加入监听)
    conn_map[conn_id] = conn;
    conn_id++;
}

int main()
{
    TcpSocket ser;
    ser.Build_Server_Socket(8888,"127.0.0.1");//设置监听端口和IP地址    
    EventLoop loop;//创建事件循环(epoll监听)
    Channel socket_listen_channel(&loop,ser.Fd());//监听套接字构建Channel
    socket_listen_channel.SetReadCallback(std::bind(Acceptor,&loop,&socket_listen_channel));//设置监听套接字的回调函数为Acceptor
    loop.AddEvent(&socket_listen_channel);//将监听套接字添加进事件循环
    /*
    注意，这里需要先add监听套接字，然后再修改监听套接字的监控事件为读事件，最后一定要modify;
    这里的socket_listen_channel默认构造里默认的监控事件_event为0，所以这里需要先add监听套接字，然后再修改监听套接字的监控事件为读事件，最后一定要modify;
    如果不先add监听套接字，则后面的modify会失败，因为监听套接字还没有加入到epoll中(modify会判断是否在epoll中，不在则不执行);
    */
    socket_listen_channel.EnableReading();//将监听套接字设置为可读事件设置进epoll监听
    socket_listen_channel.GetLoop()->ModifyEvent(&socket_listen_channel);//修改监听套接字的事件
    while(true){
        loop.Start();//启动事件循环
    }

    return 0;
}


